package sistemadeproyectos;

import java.sql.*;
import java.io.*;
import java.net.Socket;
import java.util.*;

import java.awt.Image;
import javax.swing.ImageIcon;

/**
 * @author Antonio Mundo, 06-39956
 * @author Giancarlo Nebiolo, 06-39965
 * @author Angel Rubio, 06-40263
 * @version 2.0
 * 
 */
public class Repositorio_thr extends Thread{
    /**Socket del cliente creado al aceptar la conexion por parte del servidor**/
    private Socket sock_cliente;
    
    /**Salida de datos asociada al socket del cliente**/
    private ObjectOutputStream salida;

    /**Entrada de datos asociada al socket del cliente**/
    private ObjectInputStream entrada;
    
    /* Contiene la ruta absoluta del directorio que tiene abierto este hilo */
    private String ruta_trabajo = "";
    
    /* Contiene la ruta raiz hasta la que tiene permiso el repositorio de mostrar
     * esto significa que no se puede retroceder mas alla de esta ruta*/
    private String ruta_raiz = "";
            
    /* Contiene el nombre de la carpeta del equipo al usuario que realizao la 
     * conexion.*/
    private String equipo = "";
    
    /* Contiene el identificador del usuario que realizo la conexion */
    private String usuario = "";
    
    /* Directorio del servidor al que accedio el usuario, no puede ser una 
     * carpeta  */
    private File dir_trabajo = null;

    private boolean continuar = true;
    
    
    private String barra(){
        String resultado = "";
        if(System.getProperty("os.name").substring(0,5).equals("Windo")){
            resultado = "\\";
        }
        else if(System.getProperty("os.name").substring(0,5).equals("Linux")){
            resultado = "/";
        }
        return resultado;
    }
    
    
   /**
     * Constructor de la clase AdminChat_thr
     * @param socket socket recibido del ServerSocket luego de aceptar una 
     * comunicacion con el cliente.
     */
    public Repositorio_thr (Socket socket) {
        this.sock_cliente = socket;
    }
    
    private String limpiarBarras(String original){
        String resultado = "";
        if(System.getProperty("os.name").substring(0,5).equals("Windo")){
            for(int i = 0; i < original.length(); i++){
                if((original.charAt(i)+"").equals("/")){
                    resultado = resultado + "\\";
                }
                else{
                    resultado = resultado + original.charAt(i)+"";
                }
            }
        }
        else if(System.getProperty("os.name").substring(0,5).equals("Linux")){
            for(int i = 0; i < original.length(); i++){
                if((original.charAt(i)+"").equals("\\")){
                    resultado = resultado + "/";
                }
                else{
                    resultado = resultado + original.charAt(i)+"";
                }
            }
        }
        return resultado;
    }
    
    private ImageIcon ajustarImagen(String ico, int ancho, int alto) {
        ImageIcon tmpIconAux = null;
        if(System.getProperty("os.name").substring(0,5).equals("Windo")){
            tmpIconAux = new ImageIcon("C:\\Colaborativo\\"+limpiarBarras(ico));
        }
        else if(System.getProperty("os.name").substring(0,5).equals("Linux")){
            tmpIconAux = new ImageIcon("/home/giancarlo/Colaborativo/"+limpiarBarras(ico));
        }
        
        
        //Escalar Imagen
        ImageIcon tmpIcon = new ImageIcon(tmpIconAux.getImage().getScaledInstance(ancho, alto, Image.SCALE_DEFAULT));
        return tmpIcon;
    }
    
    public void cerrar(){
        this.continuar = false;
        limpiar_socket();
    }
    
    private static String modificado(long tiempo) {
        java.util.Date d = new java.util.Date(tiempo);
        Calendar c = new GregorianCalendar();
        c.setTime(d);
        
        String dia = Integer.toString(c.get(Calendar.DATE));
        String mes = Integer.toString(c.get(Calendar.MONTH));
        String annio = Integer.toString(c.get(Calendar.YEAR));
        String hora = Integer.toString(c.get(Calendar.HOUR_OF_DAY));
        String minuto = Integer.toString(c.get(Calendar.MINUTE));
        String segundo = Integer.toString(c.get(Calendar.SECOND));
        
        return (dia+"/"+mes+"/"+annio+" "+hora+":"+minuto+":"+segundo);
    }
        
    private void listar_archivos()
            throws IOException
    {
        File[] ficheros = (this.dir_trabajo).listFiles();
        ArrayList<String> list_archivos = new ArrayList<String>();
        for (int x=0;x<ficheros.length;x++)
        {
               String archivo= ficheros[x].getName();
               if (ficheros[x].isDirectory()){
                   archivo = archivo+barra(); 
               }
               list_archivos.add(archivo);
        }
        
        (this.salida).writeObject(list_archivos);
    }        
    
    private void descargar_archivo(String ruta_arch) throws IOException {
        FileInputStream desc_arch = new FileInputStream(ruta_arch);  
                        
        /* Creo la estructura que voy a transmitir con los bytes
         * del archivo.*/
        byte[] arr = new byte[desc_arch.available()];
        desc_arch.read(arr);
                        
        /* Transmito el archivo */
        (this.salida).writeObject(arr);                                 
        desc_arch.close();
    }
    
    // PROCEDIMIENTO RECURSIVO; SE PUEDE MEJORAR EFICIENCIA VOLVIENDOLO ITERATIVO
    // GUARDAR RUTA ABSOLUTA DE DIRECTORIOS EN PILA E IRLOS PROCESANDO 1 POR 1
    private void Borrar_archivo(File arch_eliminar) throws IOException {
        if (!(arch_eliminar.isDirectory())) {
            /* Variables para actualizar el log de la base de datos */
            String[] tiempo_mod = (modificado(arch_eliminar.lastModified())).split(" "); 
            String fecha_mod = tiempo_mod[0];
            String hora_mod = tiempo_mod[1];
            String nombre_arch = arch_eliminar.getName();
            String ruta_completa = arch_eliminar.getAbsolutePath();
            double longitud = (arch_eliminar.length())/1024;  
 
            //Borramos el archivo
            arch_eliminar.delete();
 
            /* Estructura del Arreglo 
             * equipo, usuario, nombre_archivo, fecha_carga, hora_carga, 
             * longitud_archivo, ruta_absoluta
             */
            String datos[] = {(this.equipo),(this.usuario),nombre_arch,fecha_mod,
                hora_mod,ruta_completa};
            //Borramos la entrada de la base de datos
            try { trans_basedatos(false, datos, longitud); }
            catch (Exception e) { 
                System.err.println("Fallo operacion de archivos.");
                /*DEBEMOS CONSIDERAR EL PROBLEMA QUE OCURRE
                AL NO PODER BORRAR LA BASE DE DATOS*/
            }
        }
        else {
            File[] ficheros = arch_eliminar.listFiles();
            for (int x=0;x<ficheros.length;x++)
            { Borrar_archivo(ficheros[x]); }
            arch_eliminar.delete();
        }
    }
    
    private void cargar_archivo (byte[] arch_cont, String nombre_arch) 
            throws IOException, ClassNotFoundException, SQLException {
        /** Creamos un archivo vacio en el servidor con el nombre especificado  
          * y escribimos los bytes del archivo que se quiere cargar.
          */
        String ruta_completa = (this.ruta_trabajo)+barra()+nombre_arch;
        File arch_subido = new File(ruta_completa);
        FileOutputStream arch_carga;
        arch_carga = new FileOutputStream(arch_subido);
                        
        /**Creamos un apuntador a ese archivo para escribir los bytes*/
        BufferedOutputStream buffout = new BufferedOutputStream(arch_carga);

        /* Escribimos los bytes del archivo que se subio y lo cerramos */
        buffout.write(arch_cont); 
        buffout.flush();
        buffout.close();
        
        /* Actualizamos el log de la base de datos */
        String[] tiempo_mod = (modificado(arch_subido.lastModified())).split(" "); 
        String fecha_mod = tiempo_mod[0];
        String hora_mod = tiempo_mod[1];
        double longitud = (arch_subido.length())/1024;  
        /* Estructura del Arreglo 
         * equipo, usuario, nombre_archivo, fecha_carga, hora_carga, 
         * longitud_archivo, ruta_absoluta
         */
        String datos[] = {(this.equipo),(this.usuario),nombre_arch,fecha_mod,
            hora_mod,ruta_completa};
        trans_basedatos(true, datos, longitud);
    } 
        
            
    /* Si operacion en true se actualizo o inserto, si es false se elimino */
    private void trans_basedatos(boolean Operacion, String[] datos, double longitud) 
            throws ClassNotFoundException, SQLException {
        /* Inicializamos los valores que vamos
         * a actualizar  o eliminar en el log de la base de Datos 
         */                
        BDAdmin motor = new BDAdmin("SELECT ruta_absoluta " + "FROM archivo_repositorio "
             + "WHERE ruta_absoluta = " + "\'"+datos[5]+"\'");
        ResultSet rs = motor.consulta();
        
        if (Operacion == true) 
        {
            /* Si el archivo ya existia actualizamos la base de 
             * datos de lo contrario insertamos una entrada.*/    
            if (rs.next() == true) 
            { 
              motor.setQuery("UPDATE archivo_repositorio "
                 + "SET usuario = \'"+datos[1]+"\'"+ ", longitud_archivo = " 
                 + longitud+", fecha_carga = \'"+datos[3]+"\'" 
                 + ", hora_carga = \'"+datos[4]+"\'"+ ", estado = \'Modificado\'"
                 + " WHERE ruta_absoluta = " + "\'"+datos[5]+"\'");
              motor.actualizar();
            }
            else {
              motor.setQuery("INSERT INTO archivo_repositorio("
                 + "equipo, usuario, nombre_archivo, fecha_carga, "
                 + "hora_carga, longitud_archivo, ruta_absoluta, estado) VALUES("
                 + "\'"+datos[0]+"\', \'"+datos[1]+"\', \'"+datos[2]+"\', \'" 
                 + datos[3]+"\', \'"+datos[4]+"\', "+longitud
                 + ", \'"+datos[5]+"\', "+"\'Nuevo\')");  
              motor.actualizar();
            } 
        }
        else {
            /* Si el archivo ya existia actualizamos la base de 
             * datos de lo contrario insertamos una entrada.*/
            if (rs.next() == true) 
            { 
              motor.setQuery("UPDATE archivo_repositorio "
                 + "SET usuario = \'"+datos[1]+"\'"+ ", longitud_archivo = " 
                 + longitud+", fecha_carga = \'"+datos[3]+"\'" 
                 + ", hora_carga = \'"+datos[4]+"\'"+ ", estado = \'Eliminado\'"
                 + " WHERE ruta_absoluta = " + "\'"+datos[5]+"\'");
              motor.actualizar();
            }
        }
    }    
            
        
   /**
     * Cierra el socket; asi como la salida y entrada de datos del mismo.
     */
    private void limpiar_socket() {
        try { 
          entrada.close();
          salida.close();
          sock_cliente.close();
        } catch (Exception e) {}
    }
    
            
    /**Metodo que iniciara la ejecucion de este hilo. Su funcion sera la 
     * de procesar cada nuevo usuario, mostrandole la lista de salas de chat
     * actualmente creadas y agregandolo a la sala de chat que 
     * especifique, o terminando la conexion si asi o deseo el usuario;
     * una vez echo esto se encargara de mantenerse leyendo por mensajes de este
     * cliente y de retransmitirlos a los demas usuarios de la sala de chat**/
    public void run(){
        try {
            /* Inicializamos la salida y la entrada del socket */
            this.salida = new ObjectOutputStream((this.sock_cliente).getOutputStream());
            this.entrada = new ObjectInputStream((this.sock_cliente).getInputStream());
                        
            /* Intentamos la identificacion del usuario, el equipo al cual 
             * esta conectado y terminamos de inicializar. 
             * OJO 
             * a) La ruta "/home/..." es para efectos de prueba.
             * b) Asumimos que la ruta_trabajo no lleva un "/" al final 
             * para simplificar retroceso en un directorio.
             */
            (this.usuario) = (String)(this.entrada).readObject();
            (this.equipo) = (String)(this.entrada).readObject();
            if(System.getProperty("os.name").substring(0,5).equals("Windo")){
                (this.ruta_raiz) = "C:\\Colaborativo\\"+(this.equipo); 
            }
            else if(System.getProperty("os.name").substring(0,5).equals("Linux")){
                (this.ruta_raiz) = "/home/giancarlo/Colaborativo/"+(this.equipo); 
            }
            
            (this.ruta_trabajo) = (this.ruta_raiz);
            
            /* Variable booleana que nos indica si estamos en la 
             * carpeta del equipo o en una carpeta anidada */
            boolean carpeta_raiz = true;
                    
            /* Abrimos el directorio del equipo al que esta conectado el cliente 
             * Si no existe es creado, FALTA MANEJAR PROBLEMA DE SI NO SE CREA LA
             * CARPETA DEL EQUIPO*/
            (this.dir_trabajo) = new File(ruta_trabajo);
            if (!(this.dir_trabajo).exists()) {
                (this.dir_trabajo).mkdir();
            }
            
            /* Listamos los Archivos */
            listar_archivos();
            
            /* Inicializamos variables que usamos dentro del proceso de 
             * transacciones del usuario */
            String eleccion = ""; /* Indica el tipo de transaccion iniciado por el usuario */ 
            String nombre_arch = ""; /* Guarda el nombre de un archivo especificado por el cliente */
            while (this.continuar)
            {
               /**Intentamos leer el comando del usuario*/
               eleccion = (String)(this.entrada).readObject();
                        
               if (eleccion != null)
               {
                    /* Si el usuario abrio una carpeta anidada 
                     * cambiamos el directorio de trabajo a la carpeta 
                     * anidada y trabajamos desde ahi
                     */
                    if (eleccion.equals("abrir")) {
                        /* Obtenemos la ruta, abrimos la carpeta y establecemos
                         * que no estamos en la carpeta raiz
                         * OJO asumimos que la ruta de trabajo no termina con "/"
                         */
                        nombre_arch = (String)(this.entrada).readObject();
                        (this.ruta_trabajo) = (this.ruta_trabajo) + barra() + nombre_arch;                                
                        
                        System.out.println("La ruta pedida es: " + this.ruta_trabajo);
                        
                        (this.dir_trabajo) = new File((this.ruta_trabajo));
                        carpeta_raiz = false;
                        
                        /* Listamos los archivos */
                        listar_archivos();
                        
                    }
                    
                    /* Si el usuario retrocedio de carpeta verificamos
                     * si la carpeta en la que se encuentra es la carpeta
                     * raiz del equipo, si es asi devuelve una lista con 
                     * elemento "/denegado/" para indicar al cliente que no
                     * puede retroceder mas
                     */
                    else if (eleccion.equals("atras")) {
                        if (carpeta_raiz == false)
                        {
                            if ((dir_trabajo.getParent()).equals(this.ruta_raiz))
                            { carpeta_raiz = true; }
                            
                            /* Reconstruyo la ruta de trabajo eliminando la
                             * ultima carpeta anidada.
                             */
                            int delimitador = (this.ruta_trabajo).lastIndexOf(barra()); 
                            (this.ruta_trabajo) = (this.ruta_trabajo).substring(0,delimitador);
                            
                            /* Abro la nueva ruta de trabajo y envio al cliente
                             * la lista de archivos
                             */
                            (this.dir_trabajo) = new File((this.ruta_trabajo));
                            listar_archivos();
                        }
                        else {
                            ArrayList<String> list_arch = new ArrayList<String>();
                            list_arch.add("/denegado/");
                            (this.salida).writeObject(list_arch);
                        }
                    }
                    
                    /* Si el usuario quiere descargar un archivo abro el mismo
                     * y transmito su arreglo de bytes.*/
                    else if (eleccion.equals("descargar")) {
                        /* Leo el nombre del archivo y se lo transmito al cliente */
                        nombre_arch = (String)(this.entrada).readObject();
                        String ruta_arch = ruta_trabajo + barra() + nombre_arch;
                        descargar_archivo(ruta_arch);
                    }
                    
                    /* Si el usuario quiere subir un archivo leemos el nombre 
                     * del archivo y el arreglo de bytes asociados, y devolvemos
                     * la lista de archivos del directorio donde se cargo el archivo.
                     */
                    else if (eleccion.equals("cargar")) {
                        
                        /* Leo el nombre del archivo a cargar, y el arreglo de bytes
                         * asociados a ese archivo.
                         */
                        nombre_arch = (String)(this.entrada).readObject();
                        byte[] arr = (byte[])(this.entrada).readObject();
                        
                        /* Cargo el archivo en el servidor, actualizamos el
                         * log de la base de datos y devolvemos la nueva
                         * lista de archivos al cliente.
                         */
                        cargar_archivo(arr, nombre_arch);                                                
                        listar_archivos();
                    }
                    
                    /* Si el usuario quiere eliminar un archivo leemos el nombre 
                     * del archivo lo eliminamos actualizamos el log de la base de datos
                     * y devolvemos la lista de archivos del directorio donde se cargo el archivo.
                     * Por ahora no elimina carpetas.
                     */
                    else if (eleccion.equals("eliminar")) {
                        /* Leo el nombre del archivo a eliminar, 
                         * construyo la ruta del archivo y lo abro.*/
                        nombre_arch = (String)(this.entrada).readObject();
                        String ruta_arch = ruta_trabajo + barra() + nombre_arch;
                        File arch_eliminar = new File(ruta_arch);
                        
                        /* Borramos el archivo, actualizamos la base de datos 
                         * y devolvemos la nueva lista de los nuevos archivos al cliente 
                         */
                        Borrar_archivo(arch_eliminar);
                        listar_archivos();
                        
                        /* POR AHORA NO MODIFICAMOS LA BASE DE DATOS */                                                   
                    }

                    /* Si el usuario quiere crear una nueva carpeta
                     * leemos el nombre de la misma la creamos y devolvemos la lista 
                     * de archivos del directorio donde se cargo el archivo.
                     */
                    else if (eleccion.equals("carpeta")) {
                        /* Leo el nombre del archivo a eliminar, 
                         * construyo la ruta del archivo y lo abro.*/
                        nombre_arch = (String)(this.entrada).readObject();
                        String ruta_arch = (this.dir_trabajo) + barra() + nombre_arch;
                        File carpeta = new File(ruta_arch);
                        
                        /* Creo la carpeta y devolvemos la nueva
                         * lista de archivos al cliente. */
                        carpeta.mkdir();
                        listar_archivos();                        
                    }
                    
                    else if (eleccion.equals("carpetaActual")){
                        (this.salida).writeObject(this.ruta_trabajo);
                    }
                            
                    else if (eleccion.equals("imagen")){
                        nombre_arch = (String)(this.entrada).readObject();
                        System.out.println("Imagen solicitada: " + nombre_arch);
                        String anchoS = (String)(this.entrada).readObject();
                        String altoS = (String)(this.entrada).readObject();
                        int ancho = Integer.parseInt(anchoS);
                        int alto = Integer.parseInt(altoS);
                        ImageIcon imagenPedida = ajustarImagen(nombre_arch, ancho, alto);
                        (this.salida).writeObject(imagenPedida);
                    }        
                                        
                    else if (eleccion.equals("salir")) { this.continuar = false; }
                    
               }
               else { 
                   this.continuar = false;
                   System.err.println("Error en Protocolo se recibio el comando nulo.");
               }     
            }
            /* Cerramos el socket y terminamos la conexion */
            limpiar_socket();
        }
        catch (Exception e) {
            limpiar_socket(); 
        }
    }
}